home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / unixcpio.gz / unixnet.cpio / ax25cmd.c < prev    next >
C/C++ Source or Header  |  1994-07-11  |  14KB  |  708 lines

  1. #include <stdio.h>
  2. #include "global.h"
  3. #include "config.h"
  4. #include "mbuf.h"
  5. #include "ax25.h"
  6. #include "ax_mbx.h"
  7. #include "timer.h"
  8. #include "netuser.h"
  9. #include "ftp.h"
  10. #include "tcp.h"
  11. #include "telnet.h"
  12. #include "iface.h"
  13. #include "lapb.h"
  14. #include "finger.h"
  15. #include "cmdparse.h"
  16. #include "session.h"
  17. #include "nr4.h"
  18. #ifdef    UNIX
  19. #include <string.h>
  20. #include <sys/types.h>
  21. time_t time();
  22. #endif
  23. /* heard stuff */
  24. #include "heard.h"
  25. /* heard stuff */
  26.  
  27. char *ax25states[] = {
  28.     "Disconnected",
  29.     "Conn pending",
  30.     "Disc pending",
  31.     "Connected",
  32.     "Recovery",
  33.     "Frame Reject",
  34. };
  35.  
  36. int domycall(),dodigipeat(),doaxstat(),dot1(),dot2(),dot3(),domaxframe(),
  37.     doaxwindow(),dopaclen(),don2(),doaxreset(),dopthresh(),doheard();
  38. #ifdef SID2
  39. int dobbscall();
  40. extern struct ax25_addr bbscall;
  41. #endif
  42.  
  43. static struct cmds axcmds[] = {
  44.     "digipeat",    dodigipeat,    0, NULLCHAR,    NULLCHAR,
  45.     "maxframe",    domaxframe,    0, NULLCHAR,    NULLCHAR,
  46.     "mycall",    domycall,    0, NULLCHAR,    NULLCHAR,
  47. #ifdef SID2
  48.     "bbscall",    dobbscall,    0, NULLCHAR,    NULLCHAR,
  49. #endif
  50.     "paclen",    dopaclen,    0, NULLCHAR,    NULLCHAR,
  51.     "pthresh",    dopthresh,    0, NULLCHAR,    NULLCHAR,
  52.     "reset",    doaxreset,    2, "ax25 reset <axcb>", NULLCHAR,
  53.     "retry",    don2,        0, NULLCHAR,    NULLCHAR,
  54.     "status",    doaxstat,    0, NULLCHAR,    NULLCHAR,
  55.     "t1",        dot1,        0, NULLCHAR,    NULLCHAR,
  56.     "t2",        dot2,        0, NULLCHAR,    NULLCHAR,
  57.     "t3",        dot3,        0, NULLCHAR,    NULLCHAR,
  58.     "window",    doaxwindow,    0, NULLCHAR,    NULLCHAR,
  59. /* heard stuff */
  60.     "heard",    doheard,    0, NULLCHAR,    NULLCHAR,
  61. /* heard stuff */
  62. #ifdef SID2
  63.     NULLCHAR,    NULLFP,        0, "ax25 subcommands: bbscall digipeat heard maxframe mycall paclen pthresh reset\n\t\tretry status t1 t2 t3 window",    NULLCHAR,
  64. #else
  65.     NULLCHAR,    NULLFP,        0, "ax25 subcommands: digipeat heard maxframe mycall paclen pthresh reset retry\n\t\tstatus t1 t2 t3 window",    NULLCHAR,
  66. #endif
  67. };
  68.  
  69. /* Multiplexer for top-level ax25 command */
  70. doax25(argc,argv)
  71. int argc;
  72. char *argv[];
  73. {
  74.     return subcmd(axcmds,argc,argv);
  75. }
  76.  
  77. /*ARGSUSED*/
  78. static
  79. doaxreset(argc,argv)
  80. int argc;
  81. char *argv[];
  82. {
  83.     struct ax25_cb *axp;
  84.     extern char notval[];
  85.  
  86.     axp = (struct ax25_cb *)htol(argv[1]);
  87.     if(!ax25val(axp)){
  88.         printf(notval);
  89.         return 1;
  90.     }
  91.     reset_ax25(axp);
  92.     return 0;
  93. }
  94.  
  95. /* Display AX.25 link level control blocks */
  96. static
  97. doaxstat(argc,argv)
  98. int argc;
  99. char *argv[];
  100. {
  101.     register int i;
  102.     register struct ax25_cb *axp;
  103.     char tmp[10];
  104.     extern char notval[];
  105.  
  106.     if(argc < 2){
  107.         printf("    &AXB IF   Snd-Q   Rcv-Q   Remote    State\n");
  108.         for(i=0;i<NHASH;i++){
  109.             for(axp = ax25_cb[i];axp != NULLAX25; axp = axp->next){
  110.                 pax25(tmp,&axp->addr.dest);
  111.                 printf("%8lx %-5s%-8d%-8d%-10s%s\n",
  112.                     (long)axp,axp->interface->name,
  113.                     len_q(axp->txq),len_mbuf(axp->rxq),
  114.                     tmp,ax25states[axp->state]);
  115.             }
  116.         }
  117.         return 0;
  118.     }
  119.     axp = (struct ax25_cb *)htol(argv[1]);
  120.     if(!ax25val(axp)){
  121.         printf(notval);
  122.         return 1;
  123.     }
  124.     dumpstat(axp);
  125.     return 0;
  126. }
  127. /* Dump one control block */
  128. static
  129. dumpstat(axp)
  130. register struct ax25_cb *axp;
  131. {
  132.     char tmp[10];
  133.     int i;
  134.  
  135.     if(axp == NULLAX25 || axp->interface == NULLIF)
  136.         return;
  137.     printf("&AXB IF   Remote   RB V(S) V(R) Unack P Retry State\n");
  138.     pax25(tmp,&axp->addr.dest);
  139.     printf("%4x %-5s%-9s",(int)axp,axp->interface->name,tmp);
  140.     putchar(axp->rejsent ? 'R' : ' ');
  141.     putchar(axp->remotebusy ? 'B' : ' ');
  142.     printf(" %4d %4d",axp->vs,axp->vr);
  143.     printf(" %02u/%02u %u",axp->unack,axp->maxframe,axp->proto);
  144.     printf(" %02u/%02u",axp->retries,axp->n2);
  145.     printf(" %s\n",ax25states[axp->state]);
  146.  
  147.     printf("T1: ");
  148.     if(run_timer(&axp->t1))
  149.         printf("%lu",(axp->t1.start - axp->t1.count) * MSPTICK);
  150.     else
  151.         printf("stop");
  152.     printf("/%lu ms; ",axp->t1.start * MSPTICK);
  153.  
  154.     printf("T2: ");
  155.     if(run_timer(&axp->t2))
  156.         printf("%lu",(axp->t2.start - axp->t2.count) * MSPTICK);
  157.     else
  158.         printf("stop");
  159.     printf("/%lu ms; ",axp->t2.start * MSPTICK);
  160.  
  161.     printf("T3: ");
  162.     if(run_timer(&axp->t3))
  163.         printf("%lu",(axp->t3.start - axp->t3.count) * MSPTICK);
  164.     else
  165.         printf("stop");
  166.     printf("/%lu ms\n",axp->t3.start * MSPTICK);
  167.  
  168.     if(axp->addr.ndigis == 0)
  169.         return;
  170.     printf("Digipeaters:");
  171.     for(i=0;i<axp->addr.ndigis;i++){
  172.         pax25(tmp,&axp->addr.digis[i]);
  173.         printf(" %s",tmp);
  174.     }
  175.     printf("\n");
  176. }
  177.  
  178. /* Display or change our AX.25 address */
  179. static
  180. domycall(argc,argv)
  181. int argc;
  182. char *argv[];
  183. {
  184.     char buf[15];
  185.  
  186.     if(argc < 2){
  187.         pax25(buf,&mycall);
  188.         printf("%s\n",buf);
  189.         return 0;
  190.     }
  191.     if(setcall(&mycall,argv[1]) == -1)
  192.         return -1;
  193.     mycall.ssid |= E;
  194.     return 0;
  195. }
  196.  
  197. #ifdef SID2
  198. /* Display or change our AX.25 BBS address */
  199. static
  200. dobbscall(argc,argv)
  201. int argc;
  202. char *argv[];
  203. {
  204.     char buf[15];
  205.  
  206.     if(argc < 2){
  207.         pax25(buf,&bbscall);
  208.         printf("%s\n",buf);
  209.         return 0;
  210.     }
  211.     if(setcall(&bbscall,argv[1]) == -1)
  212.         return -1;
  213.     bbscall.ssid |= E;
  214.     return 0;
  215. }
  216. #endif
  217.  
  218. /* Control AX.25 digipeating */
  219. static
  220. dodigipeat(argc,argv)
  221. int argc;
  222. char *argv[];
  223. {
  224.     extern int digipeat;
  225.  
  226.     if(argc == 1) {
  227.         printf("digipeat %s\n",digipeat ? "on" : "off");
  228.     } else {
  229.         if(strcmp(argv[1],"on") == 0)
  230.             digipeat = 1;
  231.         else
  232.             digipeat = 0;
  233.     }
  234. }
  235.  
  236. /* Set retransmission timer */
  237. static
  238. dot1(argc,argv)
  239. int argc;
  240. char *argv[];
  241. {
  242.     extern int16 t1init;
  243.     long atol();
  244.  
  245.     if(argc == 1) {
  246.         printf("T1 %lu ms\n",(long)t1init * MSPTICK);
  247.     } else {
  248.         t1init = (int16)atol(argv[1]) / MSPTICK;
  249.     }
  250. }
  251.  
  252. /* Set acknowledgement delay timer */
  253. static
  254. dot2(argc,argv)
  255. int argc;
  256. char *argv[];
  257. {
  258.     extern int16 t2init;
  259.     long atol();
  260.  
  261.     if(argc == 1) {
  262.         printf("T2 %lu ms\n",(long)t2init * MSPTICK);
  263.     } else {
  264.         t2init = (int16)atol(argv[1]) / MSPTICK;
  265.     }
  266. }
  267.  
  268. /* Set idle timer */
  269. static
  270. dot3(argc,argv)
  271. int argc;
  272. char *argv[];
  273. {
  274.     extern int16 t3init;
  275.     long atol();
  276.  
  277.     if(argc == 1) {
  278.         printf("T3 %lu ms\n",(long)t3init * MSPTICK);
  279.     } else {
  280.         t3init = (int16)atol(argv[1]) / MSPTICK;
  281.     }
  282. }
  283.  
  284. /* Set retry limit count */
  285. static
  286. don2(argc,argv)
  287. int argc;
  288. char *argv[];
  289. {
  290.     extern int16 n2;
  291.  
  292.     if(argc == 1) {
  293.         printf("Retry %u\n",n2);
  294.     } else {
  295.         n2 = atoi(argv[1]);
  296.     }
  297. }
  298.  
  299. /* Set maximum number of frames that will be allowed in flight */
  300. static
  301. domaxframe(argc,argv)
  302. int argc;
  303. char *argv[];
  304. {
  305.     extern int16 maxframe;
  306.  
  307.     if(argc == 1) {
  308.         printf("Maxframe %u\n",maxframe);
  309.     } else {
  310.         maxframe = atoi(argv[1]);
  311.     }
  312. }
  313.  
  314. /* Set maximum length of I-frame data field */
  315. static
  316. dopaclen(argc,argv)
  317. int argc;
  318. char *argv[];
  319. {
  320.     extern int16 paclen;
  321.  
  322.     if(argc == 1) {
  323.         printf("Paclen %u\n",paclen);
  324.     } else {
  325.         paclen = atoi(argv[1]);
  326.     }
  327. }
  328. /* Set size of I-frame above which polls will be sent after a timeout */
  329. static
  330. dopthresh(argc,argv)
  331. int argc;
  332. char *argv[];
  333. {
  334.     extern int16 pthresh;
  335.  
  336.     if(argc == 1) {
  337.         printf("Pthresh %u\n",pthresh);
  338.     } else {
  339.         pthresh = atoi(argv[1]);
  340.     }
  341. }
  342.  
  343. /* Set high water mark on receive queue that triggers RNR */
  344. static
  345. doaxwindow(argc,argv)
  346. int argc;
  347. char *argv[];
  348. {
  349.     extern int16 axwindow;
  350.  
  351.     if(argc == 1) {
  352.         printf("Axwindow %u\n",axwindow);
  353.     } else {
  354.         axwindow = atoi(argv[1]);
  355.     }
  356. }
  357. /* End of ax25 subcommands */
  358.  
  359. /* Initiate interactive AX.25 connect to remote station */
  360. doconnect(argc,argv)
  361. int argc;
  362. char *argv[];
  363.  {
  364.     void ax_rx(),ax_tx(),ax_state();
  365.     int ax_parse();
  366.     struct ax25_addr dest;
  367.     struct ax25 addr;
  368.     struct ax25_cb *open_ax25();
  369.     struct interface *ifp;
  370.     struct session *s;
  371.     extern int16 axwindow;
  372.     int i;
  373.  
  374.     if (strcmp(argv[1],"netrom") == 0) {
  375.         printf("Connect on netrom interface not supported\n") ;
  376.         return 1 ;
  377.     }
  378.     
  379.     for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
  380.         if(strcmp(argv[1],ifp->name) == 0)
  381.             break;
  382.  
  383.     if(ifp == NULLIF){
  384.         printf("Interface %s unknown\n",argv[1]);
  385.         return 1;
  386.     }
  387.     setcall(&dest,argv[2]);
  388.     /* See if a session already exists */
  389.     for(s = sessions; s < &sessions[nsessions]; s++){
  390.         if(s->type == AX25TNC
  391.          && addreq(&s->cb.ax25_cb->addr.dest,&dest)){
  392. #if    (defined(MAC) || defined(AMIGA))
  393.             printf("Session %lu to %s already exists\n",
  394. #else
  395.             printf("Session %u to %s already exists\n",
  396. #endif
  397.                 s - sessions,argv[2]);
  398.             return 1;
  399.         }
  400.     }
  401.     /* Allocate a session descriptor */
  402.     if((s = newsession()) == NULLSESSION){
  403.         printf("Too many sessions\n");
  404.         return 1;
  405.     }
  406.     if((s->name = malloc((unsigned)strlen(argv[2])+1)) != NULLCHAR)
  407.         strcpy(s->name,argv[2]);
  408.     s->type = AX25TNC;
  409.     s->parse = ax_parse;
  410.     current = s;
  411.     ASSIGN(addr.source,mycall);
  412.     setcall(&addr.dest,argv[2]);
  413.     for(i=3; i < argc; i++)
  414.         setcall(&addr.digis[i-3],argv[i]);
  415.  
  416.     addr.ndigis = i - 3;
  417.     s->cb.ax25_cb = open_ax25(&addr,axwindow,ax_rx,ax_tx,ax_state,ifp,(char *)s);
  418.     go();
  419.     return 0;
  420. }
  421.  
  422.  
  423. /* Display changes in AX.25 state */
  424. void
  425. ax_state(axp,old,new)
  426. struct ax25_cb *axp;
  427. int old,new;
  428. {
  429.     struct session *s;
  430.  
  431.     s = (struct session *)axp->user;
  432.  
  433.     if(current != NULLSESSION && current->type == AX25TNC && current == s){
  434.         /* Don't print transitions between CONNECTED and RECOVERY */
  435.         if(new != RECOVERY && !(old == RECOVERY && new == CONNECTED))
  436.             printf("%s\n",ax25states[new]);
  437.         if(new == DISCONNECTED)
  438.             cmdmode();
  439.         fflush(stdout);
  440.     }
  441.     if(new == DISCONNECTED){
  442.         axp->user = NULLCHAR;
  443.         freesession(s);
  444.     }
  445. }
  446. /* Handle typed characters on AX.25 connection */
  447. int
  448. ax_parse(buf,cnt)
  449. char *buf;
  450. int16 cnt;
  451. {
  452.     struct mbuf *bp;
  453.     register char *cp;
  454.     char c;
  455.  
  456.     if(current == NULLSESSION || current->type != AX25TNC)
  457.         return;    /* "can't happen" */
  458.  
  459.     /* If recording is on, record outgoing stuff too */
  460.     if(current->record != NULLFILE)
  461.         fwrite(buf,1,(int)cnt,current->record);
  462.  
  463.     /* Allocate buffer and start it with the PID */
  464.     bp = alloc_mbuf(cnt+1);
  465.     *bp->data = PID_FIRST | PID_LAST | PID_NO_L3;
  466.     bp->cnt++;
  467.  
  468.     /* Copy keyboard buffer to output, stripping line feeds */
  469.     cp = bp->data + 1;
  470.     while(cnt-- != 0){
  471.         c = *buf++;
  472.         if(c != '\n'){
  473.             *cp++ = c;
  474.             bp->cnt++;
  475.         }
  476.     }
  477.     send_ax25(current->cb.ax25_cb,bp);
  478. }
  479. /* Handle new incoming terminal sessions
  480.  * This is the default receive upcall function, used when
  481.  * someone else connects to us
  482.  */
  483. void
  484. ax_incom(axp,cnt)
  485. register struct ax25_cb *axp;
  486. int16 cnt;
  487. {
  488.     void ax_session(), mbx_incom() ;
  489.     
  490. #ifdef    SID2
  491.     ax_session(axp, cnt);
  492. #else
  493.     if (ax25mbox)
  494.         mbx_incom(axp,cnt) ;
  495.     else
  496.         ax_session(axp,cnt) ;
  497. #endif
  498.     return ;
  499.  
  500. }
  501.  
  502. /* This function sets up an ax25 chat session */
  503. /*ARGSUSED*/
  504. void
  505. ax_session(axp,cnt)
  506. register struct ax25_cb *axp ;
  507. int16 cnt ;
  508. {
  509.     struct session *s;
  510.     char remote[10];
  511.     void ax_rx(),ax_state(),ax_tx();
  512.  
  513.     pax25(remote,&axp->addr.dest);
  514.     if((s = newsession()) == NULLSESSION){
  515.         /* Out of sessions */
  516.         disc_ax25(axp);
  517.         return;
  518.     }
  519.     s->type = AX25TNC;
  520.     s->name = malloc((int16)strlen(remote)+1);
  521.     s->cb.ax25_cb = axp;
  522.     s->parse = ax_parse;
  523.     strcpy(s->name,remote);
  524.     axp->r_upcall = ax_rx;
  525.     axp->s_upcall = ax_state;
  526.     axp->t_upcall = ax_tx;
  527.     axp->user = (char *)s;
  528. #if    (defined(MAC) || defined(AMIGA))
  529.     printf("\007Incoming AX25 session %lu from %s\n",s - sessions,remote);
  530. #else
  531.     printf("\007Incoming AX25 session %u from %s\n",s - sessions,remote);
  532. #endif
  533.     fflush(stdout);
  534. }
  535.  
  536. /* Handle incoming terminal traffic */
  537. void
  538. ax_rx(axp,cnt)
  539. struct ax25_cb *axp;
  540. int16 cnt;
  541. {
  542.     register struct mbuf *bp;
  543.     struct mbuf *recv_ax25();
  544.     char c;
  545.  
  546.     /* Hold output if we're not the current session */
  547.     if(mode != CONV_MODE || current == NULLSESSION
  548.      || current->type != AX25TNC || current->cb.ax25_cb != axp)
  549.         return;
  550.  
  551.     if((bp = recv_ax25(axp,cnt)) == NULLBUF)
  552.         return;
  553.  
  554.     /* Display received characters, translating CR's to CR/LF */
  555.     while(bp != NULLBUF){
  556.         while(bp->cnt-- != 0){
  557.             c = *bp->data++;
  558.             if(c == '\r')
  559.                 c = '\n';
  560.             putchar(c);
  561.             if(current->record){
  562. #ifndef UNIX
  563.                 if(c == '\n')
  564.                     fputc('\r',current->record);
  565. #endif
  566.                 fputc(c,current->record);
  567.             }
  568.         }
  569.         bp = free_mbuf(bp);
  570.     }
  571.     if(current->record)
  572.         fflush(current->record);
  573.     fflush(stdout);
  574. }
  575. /* Handle transmit upcalls. Used only for file uploading */
  576. void
  577. ax_tx(axp,cnt)
  578. struct ax25_cb *axp;
  579. int16 cnt;
  580. {
  581.     register char *cp;
  582.     struct session *s;
  583.     register struct mbuf *bp;
  584.     int16 size;
  585.     int c;
  586.  
  587.     if((s = (struct session *)axp->user) == NULLSESSION
  588.      || s->upload == NULLFILE)
  589.         return;
  590.     while(cnt != 0){
  591.         size = min(cnt,axp->paclen+1);
  592.         if((bp = alloc_mbuf(size)) == NULLBUF)
  593.             break;
  594.         cp = bp->data;
  595.         /* Start with the PID */
  596.         *cp++ = PID_FIRST | PID_LAST | PID_NO_L3;
  597.         bp->cnt++;
  598.  
  599.         /* Now send data characters, translating between local
  600.          * keyboard end-of-line sequences and the (unwritten)
  601.          * AX.25 convention, which is carriage-return only
  602.          */
  603.         while(bp->cnt < size){
  604.             if((c = getc(s->upload)) == EOF)
  605.                 break;
  606. #ifdef    MSDOS
  607.             /* MS-DOS gives cr-lf */
  608.             if(c == '\n')
  609.                 continue;
  610. #endif
  611. #if    (defined(UNIX) || defined(MAC) || defined(AMIGA))
  612.             /* These give lf only */
  613.             if(c == '\n')
  614.                 c = '\r';
  615. #endif
  616.             *cp++ = c;
  617.             bp->cnt++;
  618.         }    
  619.         if(bp->cnt > 1) {
  620.             send_ax25(axp,bp);
  621.         } else {
  622.             /* Empty frame, don't bother sending */
  623.             free_p(bp);
  624.             break;
  625.         }
  626.         cnt -= bp->cnt;
  627.     }
  628.     if(cnt != 0){
  629.         /* Error or end-of-file */
  630.         fclose(s->upload);
  631.         s->upload = NULLFILE;
  632.         free(s->ufile);
  633.         s->ufile = NULLCHAR;
  634.     }
  635. }
  636.  
  637. /* heard stuff */
  638. struct ax25_heard heard = {0,0};
  639. /* Display AX.25 heard list */
  640. static
  641. doheard(argc,argv)
  642. int argc;
  643. char *argv[];
  644. {
  645.     struct heard_stuff *hp;
  646.     struct ax25_addr *dp;
  647.     extern struct ax25_heard heard;
  648.     char tmp[16];
  649.     int curr;
  650.     long t;
  651.  
  652.     if(argc < 2) {
  653.         if (!heard.enabled) {
  654.             printf("not enabled\n");
  655.             return(0);
  656.         }
  657.         time(&t);
  658.         printf("\nHeard list: %s\n",ctime(&t));
  659.         curr = heard.first;
  660.         while (curr != -1) {
  661.             hp = &heard.list[curr];
  662.             pax25(tmp,&hp->info.source);
  663.             printf("%-10s",tmp);
  664.             if(hp->info.ndigis > 0){
  665.                 printf(" (via");
  666.                 /* Print digi string until packet got to us */
  667.                 for(dp = &hp->info.digis[0]; dp < &hp->info.digis[hp->info.ndigis]; dp++){
  668.                     if (dp->ssid & REPEATED) {
  669.                         pax25(tmp,dp);
  670.                         printf(" %s",tmp);
  671.                     } else {
  672.                         break;
  673.                     }
  674.                 }
  675.                 printf(")");
  676.             }
  677.             if (hp->flags) {
  678.                 printf("%-5s%-8s%-4s",
  679.                                 (hp->flags & HEARD_ARP) ? " ARP" : "",
  680.                                 (hp->flags & HEARD_NETROM) ? " NETROM" : "",
  681.                                 (hp->flags & HEARD_IP) ? " IP" : "");
  682.             }
  683.             printf("  %s", ctime((long *)&hp->htime));
  684.             curr = hp->next;
  685.         }
  686.         return 0;
  687.     }
  688.     if (strcmp(argv[1],"clear") == 0) {
  689.         heard.cnt = 0;
  690.         heard.first = -1;
  691.         return 0;
  692.     } else if (strcmp(argv[1],"on") == 0) {
  693.         if (!heard.enabled) {
  694.             heard.enabled = 1;
  695.             heard.cnt = 0;
  696.             heard.first = -1;
  697.         }
  698.         return 0;
  699.     } else if (strcmp(argv[1],"off") == 0) {
  700.         heard.enabled = 0;
  701.         return 0;
  702.     } else {
  703.         printf("Usage: ax25 heard [ on | off | clear ]\n");
  704.     }
  705.     return 0;
  706. }
  707. /* heard stuff */
  708.